﻿using DotNetty.Buffers;
using DotNetty.Codecs.Http;
using DotNetty.Common;
using DotNetty.Common.Utilities;
using DotNetty.Transport.Channels;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Security.Policy;
using System.Text;

namespace HttpShare
{
    public class HttpServerHandler:ChannelHandlerAdapter
    {

        private string Dir;

        public HttpServerHandler(string dir)
        {
            Dir = dir;
        }

        static readonly ThreadLocalCache Cache = new ThreadLocalCache();

        sealed class ThreadLocalCache : FastThreadLocal<AsciiString>
        {
            protected override AsciiString GetInitialValue()
            {
                DateTime dateTime = DateTime.UtcNow;
                return AsciiString.Cached($"{dateTime.DayOfWeek}, {dateTime:dd MMM yyyy HH:mm:ss z}");
            }
        }

        static readonly byte[] StaticPlaintext = Encoding.UTF8.GetBytes("Hello, World!");
        static readonly int StaticPlaintextLen = StaticPlaintext.Length;
        static readonly IByteBuffer PlaintextContentBuffer = Unpooled.UnreleasableBuffer(Unpooled.DirectBuffer().WriteBytes(StaticPlaintext));
        static readonly AsciiString PlaintextClheaderValue = AsciiString.Cached($"{StaticPlaintextLen}");
        static readonly AsciiString JsonClheaderValue = AsciiString.Cached($"{JsonLen()}");

        static readonly AsciiString TypeHtml = AsciiString.Cached("text/html; charset=utf-8");
        static readonly AsciiString TypePlain = AsciiString.Cached("text/plain");
        static readonly AsciiString TypeJson = AsciiString.Cached("application/json");
        static readonly AsciiString ServerName = AsciiString.Cached("Netty");
        static readonly AsciiString ContentTypeEntity = HttpHeaderNames.ContentType;
        static readonly AsciiString DateEntity = HttpHeaderNames.Date;
        static readonly AsciiString ContentLengthEntity = HttpHeaderNames.ContentLength;
        static readonly AsciiString ServerEntity = HttpHeaderNames.Server;

        volatile ICharSequence date = Cache.Value;

        static int JsonLen() => Encoding.UTF8.GetBytes(NewMessage().ToJsonFormat()).Length;

        static HttpServerMessageBody NewMessage() => new HttpServerMessageBody("Hello, World!");

        /// <summary>
        /// 接收到信息
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="message"></param>
        public override void ChannelRead(IChannelHandlerContext ctx, object message)
        {
            if (message is IHttpRequest request)
            {
                try
                {
                    this.Process(ctx, request);
                }
                finally
                {
                    ReferenceCountUtil.Release(message);
                }
            }
            else
            {
                ctx.FireChannelRead(message);
            }
        }

        void Process(IChannelHandlerContext ctx, IHttpRequest request)
        {
            //解码 url特殊字符C# 无法转化
            string uri = System.Net.WebUtility.UrlDecode(request.Uri);
            string path = Dir + uri.Substring(1);
            if (Directory.Exists(path))
            {
                var htm1 = HttpServerHTMLBody.GetDirHTML(path, uri);
                WriteResponseHTML(ctx, htm1);
            }
            else if(File.Exists(path))
            {
                responseExportFile(ctx, path);
                //var htm2 = HttpServerHTMLBody.GetHTML("文件"+path);
                //WriteResponseHTML(ctx, htm2);
            }
            else
            {
                var htm3 = HttpServerHTMLBody.GetHTML(path+"不存在");
                WriteResponseHTML(ctx, htm3);
            }
            return;


                switch (uri)
            {
                case "/plaintext":
                    this.WriteResponse(ctx, PlaintextContentBuffer.Duplicate(), TypePlain, PlaintextClheaderValue);
                    break;
                case "/json":
                    byte[] json = Encoding.UTF8.GetBytes(NewMessage().ToJsonFormat());
                    this.WriteResponse(ctx, Unpooled.WrappedBuffer(json), TypeJson, JsonClheaderValue);
                    break;
                default:
                    DirectoryInfo directory = new DirectoryInfo(Dir);
                    if (directory.Exists)
                    {
                        //存在
                        string info = "<html><body><h1>当前目录存在</h1></body></html>";
                        byte[] result = Encoding.UTF8.GetBytes(info);
                        var tmp = Unpooled.UnreleasableBuffer(Unpooled.DirectBuffer().WriteBytes(result));
                        
                        this.WriteResponse(ctx, tmp.Duplicate(), TypeHtml, AsciiString.Cached(result.Length.ToString()));
                        break;
                    }
                    else {
                        //不存在
                        string info = "<html><body><h1>当前目录不存在</h1></body></html>";
                        byte[] result = Encoding.UTF8.GetBytes(info);
                        this.WriteResponse(ctx, Unpooled.WrappedBuffer(result), TypeHtml, AsciiString.Cached(result.Length.ToString()));
                        break;

                    }

            }
        }

        void responseExportFile(IChannelHandlerContext ctx,string FilePath) {

            try
            {
                FileInfo fileInfo = new FileInfo(FilePath);
                FileStream fs = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.None);
                var fileLength = fs.Length;

                //定义response对象
                var response = new DefaultHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK);
                //设置请求头部
                HttpHeaders headers = response.Headers;
                headers.Set(HttpHeaderNames.ContentLength, fileLength);
                headers.Set(HttpHeaderNames.ContentType, "application/octet-stream; charset=UTF-8");//文件流
                headers.Set(HttpHeaderNames.ContentDisposition,
                        "attachment; filename=\"" + System.Net.WebUtility.UrlEncode(fileInfo.Name) + "\";");
                ctx.WriteAsync(response);
                ctx.WriteAsync(new DefaultFileRegion(fs, 0, fileLength));
                //ctx.WriteAndFlushAsync();
                //ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
            }
            catch (Exception)
            {

                throw;
            }
        }


        void WriteResponseHTML(IChannelHandlerContext ctx,string HTML)
        {
            byte[] result = Encoding.UTF8.GetBytes(HTML);
            this.WriteResponse(ctx, Unpooled.WrappedBuffer(result), TypeHtml, AsciiString.Cached(result.Length.ToString()));
        }

        /// <summary>
        /// 返回请求
        /// </summary>
        /// <param name="ctx"></param>
        /// <param name="buf"></param>
        /// <param name="contentType"></param>
        /// <param name="contentLength"></param>
        void WriteResponse(IChannelHandlerContext ctx, IByteBuffer buf, ICharSequence contentType, ICharSequence contentLength)
        {
            // Build the response object.
            var response = new DefaultFullHttpResponse(HttpVersion.Http11, HttpResponseStatus.OK, buf, false);
            HttpHeaders headers = response.Headers;
            headers.Set(ContentTypeEntity, contentType);
            headers.Set(ServerEntity, ServerName);
            headers.Set(DateEntity, this.date);
            headers.Set(ContentLengthEntity, contentLength);

            // Close the non-keep-alive connection after the write operation is done.
            ctx.WriteAsync(response);
        }

        /// <summary>
        /// 异常处理
        /// </summary>
        /// <param name="context"></param>
        /// <param name="exception"></param>
        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) => context.CloseAsync();

        /// <summary>
        /// 读取完成
        /// </summary>
        /// <param name="context"></param>
        public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

    }
}
